#include "statusnotifieritemsource.h"
#include "dbusproperties.h"
#include "statusnotifieriteminterface.h"
#include "systemtraytypedefs.h"
#include <QPainter>
#include <netinet/in.h>
#include "statusnotifiermodel.h"


#define ORG_UKUI_STYLE  "org.ukui.style"
#define STYLE_NAME "styleName"
#define STYLE_NAME_KEY_DARK       "ukui-dark"
#define STYLE_NAME_KEY_DEFAULT    "ukui-default"

StatusNotifierItemSource::StatusNotifierItemSource(QString service, QObject *parent) :
    QObject(parent),
    m_service(service),
    m_statusNotifierItemInterface(nullptr),
    m_menuImporter(nullptr),
    m_isTitleUpdate(true),
    m_isIconUpdate(true),
    m_isTooltipUpdate(true),
    m_isStatusUpdate(true),
    m_refreshing(false),
    m_needsReRefreshing(false),
    m_valid(true)
{
    qDBusRegisterMetaType<UKUIDBusImageStruct>();
    qDBusRegisterMetaType<UKUIDBusImageVector>();
    qDBusRegisterMetaType<UKUIDBusToolTipStruct>();

    initGsetting();

    int slash = m_service.indexOf('/');
    if (slash == -1) {
        qWarning() << "Invalid notifierItemId:" << m_service;
        m_valid = false;
        return;
    }
    QString serviceName = m_service.left(slash);
    QString servicePath = m_service.mid(slash);
    m_statusNotifierItemInterface = new org::kde::StatusNotifierItem(serviceName, servicePath,
                                                                     QDBusConnection::sessionBus(),
                                                                     this);
    m_refreshTimer.setSingleShot(true);
    m_refreshTimer.setInterval(10);
    connect(&m_refreshTimer, &QTimer::timeout, this, &StatusNotifierItemSource::performRefresh);

    if(m_valid && !serviceName.isEmpty() && m_statusNotifierItemInterface->isValid()) {
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewTitle,
                this, &StatusNotifierItemSource::refreshTitle);
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewIcon,
                this, &StatusNotifierItemSource::refreshIcons);
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewAttentionIcon,
                this, &StatusNotifierItemSource::refreshIcons);
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewOverlayIcon,
                this, &StatusNotifierItemSource::refreshIcons);
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewToolTip,
                this, &StatusNotifierItemSource::refreshToolTip);
        connect(m_statusNotifierItemInterface, &OrgKdeStatusNotifierItemInterface::NewStatus,
                this, &StatusNotifierItemSource::syncStatus);
        refresh();
//        qDebug()<<">>>> 构造函数中首次  refresh();";
    }
}

StatusNotifierItemSource::~StatusNotifierItemSource()
{
    m_statusNotifierItemInterface->deleteLater();
    if(m_themeSettings) {
        delete(m_themeSettings);
        m_themeSettings = NULL;
    }
}

void StatusNotifierItemSource::initGsetting()
{
    const QByteArray styleId(ORG_UKUI_STYLE);
    if(QGSettings::isSchemaInstalled(styleId)) {
        m_themeSettings = new QGSettings(styleId);
    }
}

void StatusNotifierItemSource::refreshTitle()
{
    m_isTitleUpdate = true;
    refresh();
}

void StatusNotifierItemSource::refreshIcons()
{
    m_isIconUpdate = true;
    refresh();
}

void StatusNotifierItemSource::refreshToolTip()
{
    m_isTooltipUpdate = true;
    refresh();
}

void StatusNotifierItemSource::refresh()
{
    if (!m_refreshTimer.isActive()) {
        m_refreshTimer.start();
    }
}

void StatusNotifierItemSource::performRefresh()
{
    qDebug()<<">>>>> StatusNotifierItemSource::performRefresh()";
    if (m_refreshing) {
        m_needsReRefreshing = true;
        return;
    }
    m_refreshing = true;
    QDBusMessage message = QDBusMessage::createMethodCall(m_statusNotifierItemInterface->service(),
                                                          m_statusNotifierItemInterface->path(),
                                                          QStringLiteral("org.freedesktop.DBus.Properties"),
                                                          QStringLiteral("GetAll"));

    message << m_statusNotifierItemInterface->interface();
    QDBusPendingCall call = m_statusNotifierItemInterface->connection().asyncCall(message);
    QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
    connect(watcher, &QDBusPendingCallWatcher::finished, this, &StatusNotifierItemSource::refreshCallback);
}

void StatusNotifierItemSource::refreshCallback(QDBusPendingCallWatcher *call)
{
    qDebug()<<">>>>>>> StatusNotifierItemSource::refreshCallback";
    m_refreshing = false;
    //在异步获取Item信息过程中，如果有属性改变信号导致需要重新获取属性信息，
    //则结束本次获取，重新执行performRefresh() 获取最新的属性
    if (m_needsReRefreshing) {
        m_needsReRefreshing = false;
        performRefresh();
        call->deleteLater();
        return;
    }

    QDBusPendingReply<QVariantMap> reply = *call;
    if (reply.isError()) {
        m_valid = false;
    } else {
        // record what has changed
        setData(StatusNotifierModel::Role::TitleChanged, m_isTitleUpdate);
        m_isTitleUpdate = false;
        setData(StatusNotifierModel::Role::IconsChanged, m_isIconUpdate);
        m_isIconUpdate = false;
        setData(StatusNotifierModel::Role::ToolTipChanged, m_isTooltipUpdate);
        m_isTooltipUpdate = false;
        setData(StatusNotifierModel::Role::StatusChanged, m_isStatusUpdate);
        m_isStatusUpdate = false;

        //IconThemePath (handle this one first, because it has an impact on others)
        QVariantMap properties = reply.argumentAt<0>();
//        QString path = properties[QStringLiteral("IconThemePath")].toString();
        setData(StatusNotifierModel::Role::IconThemePath, properties[QStringLiteral("IconThemePath")].toString());
        setData(StatusNotifierModel::Role::Category, properties[QStringLiteral("Category")].toString());
        setData(StatusNotifierModel::Role::Status, properties[QStringLiteral("Status")].toString());
        setData(StatusNotifierModel::Role::Title, properties[QStringLiteral("Title")].toString());
        setData(StatusNotifierModel::Role::Id, properties[QStringLiteral("Id")].toString());
        qDebug()<<"@@@@@@@@@@@@@@@@@@@@@@@  "<<properties[QStringLiteral("Id")].toString();
        setData(StatusNotifierModel::Role::WindowId, properties[QStringLiteral("WindowId")].toUInt());
        setData(StatusNotifierModel::Role::ItemIsMenu, properties[QStringLiteral("ItemIsMenu")].toBool());
        //Attention Movie
        setData(StatusNotifierModel::Role::AttentionMovieName, properties[QStringLiteral("AttentionMovieName")].toString());

        //Icon
        QIcon overlay;
//        QStringList overlayNames;
        UKUIDBusImageVector image;
        QIcon icon;
        QString iconName;
        properties[QStringLiteral("OverlayIconPixmap")].value<QDBusArgument>() >> image;
        if (image.isEmpty()) {
            QString iconName = properties[QStringLiteral("OverlayIconName")].toString();
            setData(StatusNotifierModel::Role::OverlayIconName, iconName);
            if (!iconName.isEmpty()) {
//                overlayNames << iconName;
                overlay = iconNameToIcon(iconName);
            }
        } else {
            overlay = imageVectorToPixmap(image);
        }

        //优先使用Pixmap属性，然后再叠加上OverlayIconPixmap之后才可以作为图标
        properties[QStringLiteral("IconPixmap")].value<QDBusArgument>() >> image;
        if (image.isEmpty()) {
            iconName = properties[QStringLiteral("IconName")].toString();
            if (!iconName.isEmpty()) {
                icon = iconNameToIcon(iconName);
                overlayIcon(&icon, &overlay); //叠加overlayIcon
            }
        } else {
            icon = imageVectorToPixmap(image);
            if (!icon.isNull() && !overlay.isNull()) {
                overlayIcon(&icon, &overlay);
            }
        }
        setData(StatusNotifierModel::Role::Icon, icon.isNull() ? QVariant() : icon);
        setData(StatusNotifierModel::Role::IconName, iconName);

        //Attention icon
        UKUIDBusImageVector attentionImage;
        QIcon attentionIcon;
        properties[QStringLiteral("AttentionIconPixmap")].value<QDBusArgument>() >> attentionImage;
        if (attentionImage.isEmpty()) {
            QString iconName = properties[QStringLiteral("AttentionIconName")].toString();
            setData(StatusNotifierModel::Role::AttentionIconName, iconName);
            if (!iconName.isEmpty()) {
                attentionIcon = iconNameToIcon(iconName);
                overlayIcon(&attentionIcon, &overlay);
            }
        } else {
            attentionIcon = imageVectorToPixmap(attentionImage);
            if (!attentionIcon.isNull() && !overlay.isNull()) {
                overlayIcon(&attentionIcon, &overlay);
            }
        }
        setData(StatusNotifierModel::Role::AttentionIcon, attentionIcon.isNull() ? QVariant() : attentionIcon);

        //ToolTip
        UKUIDBusToolTipStruct toolTip;
        properties[QStringLiteral("ToolTip")].value<QDBusArgument>() >> toolTip;
        if (toolTip.title.isEmpty()) {
            setData(StatusNotifierModel::Role::ToolTipTitle, QString());
            setData(StatusNotifierModel::Role::ToolTipSubTitle, QString());
            setData(StatusNotifierModel::Role::ToolTipIcon, QString());
        } else {
            QIcon toolTipIcon;
            if (toolTip.image.size() == 0) {  //优先使用Pixmap，image没有的话使用IconName
                toolTipIcon = iconNameToIcon(toolTip.icon);
            } else {
                toolTipIcon = imageVectorToPixmap(toolTip.image);
            }
            setData(StatusNotifierModel::Role::ToolTipTitle, toolTip.title);
            setData(StatusNotifierModel::Role::ToolTipSubTitle, toolTip.subTitle);
            if (toolTipIcon.isNull() || toolTipIcon.availableSizes().isEmpty()) {
                setData(StatusNotifierModel::Role::ToolTipIcon, QString());
            } else {
                setData(StatusNotifierModel::Role::ToolTipIcon, toolTipIcon);
            }
        }

        //Menu
        if (!m_menuImporter) {
            QString menuObjectPath = properties[QStringLiteral("Menu")].value<QDBusObjectPath>().path();
            if (!menuObjectPath.isEmpty()) {
                if (menuObjectPath == QLatin1String("/NO_DBUSMENU") || menuObjectPath.isEmpty()) {
                    qInfo() << "DBusMenu disabled for this application";
                } else {
                    m_menuImporter = new UKUIMenuImporter(m_statusNotifierItemInterface->service(), menuObjectPath, this);
                    connect(m_menuImporter, &UKUIMenuImporter::menuUpdated, this, [this]() {
                        QMenu *menu = m_menuImporter->menu();
                            //数据模型不存储菜单，需要转发给UI层
    //                        emit contextMenuReady(m_menuImporter->menu());
                    });
                }
            }
        }
    }

    emit dataUpdated(m_service, this, m_itemData);
    qDebug()<<">>>>>>>> 更新数据";
}

QPixmap StatusNotifierItemSource::UKUIDBusImageStructToPixmap(const UKUIDBusImageStruct &image) const
{
    //如果机器是小端序存储的话，转成小端序
    if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
        uint *uintBuf = (uint *) image.data.data();
        for (uint i = 0; i < image.data.size()/sizeof(uint); ++i) {
            *uintBuf = ntohl(*uintBuf);
            ++uintBuf;
        }
    }
    if (image.width == 0 || image.height == 0) {
        return QPixmap();
    }

    //avoid a deep copy of the image data
    //we need to keep a reference to the image.data alive for the lifespan of the image, even if the image is copied
    //we create a new QByteArray with a shallow copy of the original data on the heap, then delete this in the QImage cleanup
    auto dataRef = new QByteArray(image.data);
    QImage iconImage(reinterpret_cast<const uchar*>(dataRef->data()), image.width, image.height, QImage::Format_ARGB32,
            [](void* ptr) {
                delete static_cast<QByteArray*>(ptr);
            },
            dataRef);

    //黑色主题下做反白处理
    if(m_themeSettings) {
        qInfo()<<"Black theme,Icon inversion processing";
        QString style = m_themeSettings->get(STYLE_NAME).toString();
        if(style == STYLE_NAME_KEY_DARK || style == STYLE_NAME_KEY_DEFAULT) {
            getBlackThemeIcon(iconImage);
        }
    }

    return QPixmap::fromImage(iconImage);
}

// 将图像的image合成icon格式
QIcon StatusNotifierItemSource::imageVectorToPixmap(const UKUIDBusImageVector &vector) const
{
    QIcon icon;
    for (int i = 0; i<vector.size(); ++i) {
        icon.addPixmap(UKUIDBusImageStructToPixmap(vector[i]));
    }
    return icon;
}


//通过iconName获取Icon
QIcon StatusNotifierItemSource::iconNameToIcon(QString iconName) const
{
    //回头看看这里面的demo https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html

    //这里重新梳理：重点看一下SNI协议对thene的规定和freedesktop中icon的章节

    //获取托盘图标的三种方式：
    //1、优先使用主题图标；
    //2、其次，如果iconName是图标文件，则使用该文件作为图标；
    //3、最后，在/usr/share/icons/hicolor/下查找可用图标
    QIcon itemIcon;
    if (QIcon::hasThemeIcon(iconName)) {
        itemIcon = QIcon::fromTheme(iconName);

    } else if(QFile::exists(iconName)) {
        itemIcon = QIcon(iconName);
    } else {
        QVariant vv = data(StatusNotifierModel::Role::IconThemePath);
        QString ss = vv.toString();
        QString themePath = data(StatusNotifierModel::Role::IconThemePath).toString();
        if(themePath.isEmpty()) {
            qWarning()<<data(StatusNotifierModel::Role::Id).toString()<<" IconThemePath is empty!";
            themePath = "/usr/share/icons/";
        }
        QDir themeDir(themePath);
        if (themeDir.exists()) {
            if (themeDir.exists(iconName + ".png")) {  //??? 应用图标的格式不是png的怎么办？是否应该优化？
                qDebug()<<data(StatusNotifierModel::Role::Id).toString()<<" Custom icon path: "<<themeDir;
                itemIcon.addFile(themeDir.filePath(iconName + ".png"));
            }
            if (themeDir.cd("hicolor") || (themeDir.cd("icons") && themeDir.cd("hicolor"))) {
                const QStringList sizes = themeDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                for (const QString &dir : sizes) {
                    const QStringList dirs = QDir(themeDir.filePath(dir)).entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
                    for (const QString &innerDir : dirs) {
                        QString file = themeDir.absolutePath() + "/" + dir + "/" + innerDir + "/" + iconName + ".png";
                        if (QFile::exists(file)) {
                            itemIcon.addFile(file);
                            return itemIcon;
                        }
                    }
                }
            } else {
                qCritical()<<data(StatusNotifierModel::Role::Id).toString()<<" This file is not available in the theme icon path!";
            }
        } else {
            qCritical()<<data(StatusNotifierModel::Role::Id).toString()<<" IconThemePath is non-existent!";
        }
    }
    return itemIcon;
}

void StatusNotifierItemSource::getBlackThemeIcon(QImage &image) const
{
    QColor(255,255,255);
    QColor standard (31,32,34);
    for (int x = 0; x < image.width(); x++) {
        for (int y = 0; y < image.height(); y++) {
            auto color = image.pixelColor(x, y);
            if (color.alpha() > 0) {
                if(qAbs(color.red()-standard.red())<20 && qAbs(color.green()-standard.green())<20 && qAbs(color.blue()-standard.blue())<20) {
                    color.setRed(255);
                    color.setGreen(255);
                    color.setBlue(255);
                    image.setPixelColor(x, y, color);
                } else {
                    image.setPixelColor(x, y, color);
                }
            }
        }
    }
}

void StatusNotifierItemSource::overlayIcon(QIcon *icon, QIcon *overlay)
{
    //??? overlayIcon的位置是在icon的右下角，需要和设计师讨论一下
    QIcon tmp;
    QPixmap m_iconPixmap = icon->pixmap(StdSizes::SizeSmall, StdSizes::SizeSmall);
    QPainter p(&m_iconPixmap);
    const int size = StdSizes::SizeSmall/2;
    p.drawPixmap(QRect(size, size, size, size), overlay->pixmap(size, size), QRect(0,0,size,size));
    p.end();
    tmp.addPixmap(m_iconPixmap);

    /*
    //if an m_icon exactly that size wasn't found don't add it to the vector
    //如果没有找到一个大小正好相同的m_icon，请不要将其添加到向量中
    m_iconPixmap = icon->pixmap(StdSizes::SizeSmallMedium, StdSizes::SizeSmallMedium);
    if (m_iconPixmap.width() == StdSizes::SizeSmallMedium) {
        const int size = StdSizes::SizeSmall/2;
        QPainter p(&m_iconPixmap);
        p.drawPixmap(QRect(m_iconPixmap.width()-size, m_iconPixmap.height()-size, size, size), overlay->pixmap(size, size), QRect(0,0,size,size));
        p.end();
        tmp.addPixmap(m_iconPixmap);
    }

    m_iconPixmap = icon->pixmap(StdSizes::SizeMedium, StdSizes::SizeMedium);
    if (m_iconPixmap.width() == StdSizes::SizeMedium) {
        const int size = StdSizes::SizeSmall/2;
        QPainter p(&m_iconPixmap);
        p.drawPixmap(QRect(m_iconPixmap.width()-size, m_iconPixmap.height()-size, size, size), overlay->pixmap(size, size), QRect(0,0,size,size));
        p.end();
        tmp.addPixmap(m_iconPixmap);
    }

    m_iconPixmap = icon->pixmap(StdSizes::SizeLarge, StdSizes::SizeLarge);
    if (m_iconPixmap.width() == StdSizes::SizeLarge) {
        const int size = StdSizes::SizeSmall;
        QPainter p(&m_iconPixmap);
        p.drawPixmap(QRect(m_iconPixmap.width()-size, m_iconPixmap.height()-size, size, size), overlay->pixmap(size, size), QRect(0,0,size,size));
        p.end();
        tmp.addPixmap(m_iconPixmap);
    }
    */

    // We can't do 'm_icon->addPixmap()' because if 'm_icon' uses KIconEngine,
    // it will ignore the added pixmaps. This is not a bug in KIconEngine,
    // QIcon::addPixmap() doc says: "Custom m_icon engines are free to ignore
    // additionally added pixmaps".
    *icon = tmp;
    //hopefully huge and enormous not necessary right now, since it's quite costly
}

void StatusNotifierItemSource::setData(uint propertyName, QVariant propertyData)
{
    qDebug()<<"############ "<<propertyName<<propertyData;
    m_itemData.insert(propertyName, propertyData);
}

QVariant StatusNotifierItemSource::data(uint propertyName) const
{
    return  m_itemData.value(propertyName);
}

void StatusNotifierItemSource::syncStatus(QString status)
{
    setData(StatusNotifierModel::Role::TitleChanged, false);
    setData(StatusNotifierModel::Role::IconsChanged, false);
    setData(StatusNotifierModel::Role::ToolTipChanged, false);
    setData(StatusNotifierModel::Role::StatusChanged, true);
    setData(StatusNotifierModel::Role::Status, status);
    emit dataUpdated(m_service, this, m_itemData);
}


void StatusNotifierItemSource::activate(int x, int y)
{
    qDebug()<<">>>>>>StatusNotifierItemSource::activate "
           << data(StatusNotifierModel::Role::Id).toString()
           <<x<<y;
//    if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
//        QDBusMessage message = QDBusMessage::createMethodCall(m_statusNotifierItemInterface->service(),
//                                                              m_statusNotifierItemInterface->path(),
//                                                              m_statusNotifierItemInterface->interface(),
//                                                              QStringLiteral("Activate"));

//        message << x << y;
//        QDBusPendingCall call = m_statusNotifierItemInterface->connection().asyncCall(message);
//        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
//        connect(watcher, &QDBusPendingCallWatcher::finished, this, &StatusNotifierItemSource::activateCallback);
//    }
}

//void StatusNotifierItemSource::activateCallback(QDBusPendingCallWatcher *call)
//{
//    QDBusPendingReply<void> reply = *call;
//    emit activateResult(!reply.isError());
//    call->deleteLater();
//}

//void StatusNotifierItemSource::secondaryActivate(int x, int y)
//{
//    if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
//        m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("SecondaryActivate"), x, y);
//    }
//}

//void StatusNotifierItemSource::scroll(int delta, const QString &direction)
//{
//    if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
//        m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("Scroll"), delta, direction);
//    }
//}

//void StatusNotifierItemSource::contextMenu(int x, int y)
//{
//    if (m_menuImporter) {
//        m_menuImporter->updateMenu();
//    } else {
//        qWarning() << "Could not find DBusMenu interface, falling back to calling ContextMenu()";
//        if (m_statusNotifierItemInterface && m_statusNotifierItemInterface->isValid()) {
//            m_statusNotifierItemInterface->call(QDBus::NoBlock, QStringLiteral("ContextMenu"), x, y);
//        }
//    }
//}


